46 Python条件语句和循环语句
46.1 引言控制结构与算法设计
理论背景:控制流的三种基本结构
从计算科学理论的角度,任何算法都可以用三种基本控制结构表示:
- 顺序结构(Sequence): 语句按顺序执行
- 选择结构(Selection): 根据条件选择执行路径
- 循环结构(Iteration): 重复执行代码块
这三种结构构成了结构化程序设计(Structured Programming)的基础,由Edsger W. Dijkstra在1968年提出。Python通过if-elif-else、for、while等关键字实现这三种结构。
数学背景:布尔逻辑与条件判断
Python的条件判断基于布尔代数(Boolean Algebra),由George Boole在1854年创立:
| 运算 | 数学符号 | Python运算 | 交换律 | 结合律 | 分配律 |
|---|---|---|---|---|---|
| 与(AND) | \(a \land b\) | a and b |
✓ | ✓ | ✓ |
| 或(OR) | \(a \lor b\) | a or b |
✓ | ✓ | ✓ |
| 非(NOT) | \(\neg a\) | not a |
- | - | - |
短路径求值(Short-Circuit Evaluation):
# Python的and/or运算采用短路径求值
# 一旦能够确定结果,立即停止计算
True and some_function() # 会调用some_function()
False and some_function() # 不会调用some_function(),直接返回False补充说明:为什么Python没有switch-case?
Python 3.10之前没有传统的switch-case语句,这是有意的设计选择: 1. if-elif-else已经足够清晰 2. 字典映射可以替代switch-case 3. 避免C语言switch的fall-through陷阱
Python 3.10引入了match-case语句,提供了更强大的模式匹配能力。
46.2 if条件语句
# 场景:基于移动平均线的交易策略
# 定义关键价格指标
price = 18.50 # 当前股价
ma20 = 18.00 # 20日移动平均线
ma60 = 17.50 # 60日移动平均线
# if语句:条件判断与分支执行
# 语法: if condition1: elif condition2: ... else:
# if、elif、else后的冒号(:)表示代码块开始
# 条件1:价格同时高于MA20和MA60
# and逻辑运算符:两个条件都为True时,整体为True
if price > ma20 and price > ma60:
signal = '买入'
# 缩进的代码块属于if语句
# Python使用缩进(通常4个空格)表示代码块层次
# 条件2:价格高于MA20但低于MA60
# elif是"else if"的缩写,用于处理多个互斥条件
elif price > ma20:
signal = '持有'
# 条件3:价格低于MA60
elif price < ma60:
signal = '卖出'
# else:以上条件都不满足时执行
# 在这里是:价格介于MA60和MA20之间
else:
signal = '观望'
# 输出交易信号
# f-string是Python 3.6+的格式化字符串语法
print(f'交易信号: {signal}')
# 输出: 交易信号: 买入代码深度解析:
条件表达式的真值测试:
# Python中的真假值判定规则 # 以下值被判定为False: # - False、None、数值0(0, 0.0)、空字符串('')、空容器([], {}, set()) # 任何其他值都被判定为True # 这称为"真值测试"(Truth Value Testing) # 示例:检查持仓是否为空 position = 0 if position: print(f'持仓{position}股') else: print('空仓') # 会执行这个分支 # 显式比较(推荐) if position != 0: print(f'持仓{position}股') else: print('空仓')链式比较(Chained Comparison):
# Python支持链式比较,数学上更自然 price = 15.0 # 方法1:链式比较(Python风格) if 10 < price < 20: print('价格在10到20之间') # 等价于方法2:逻辑与运算 if price > 10 and price < 20: print('价格在10到20之间') # 链式比较更简洁,且price只计算一次三元运算符(Ternary Operator):
# Python的三元表达式: 值1 if 条件 else 值2 # 适用于简单的条件赋值 # 示例:计算交易手续费 amount = 10000 commission_rate = 0.0003 if amount > 10000 else 0.0005 # 如果金额>10000,费率0.03%,否则0.05% print(f'手续费率: {commission_rate:.4f}') # 等价的if-else语句 if amount > 10000: commission_rate = 0.0003 else: commission_rate = 0.0005嵌套if语句的优化:
# 不推荐:深层嵌套 price = 18.5 volume = 1000000 if price > 18: if volume > 500000: if price > ma20: signal = '强力买入' else: signal = '考虑买入' else: signal = '观望' else: signal = '不操作' # 推荐:提前返回(卫语句) def generate_signal(price, volume, ma20): if price <= 18: return '不操作' if volume <= 500000: return '观望' if price > ma20: return '强力买入' return '考虑买入' signal = generate_signal(price, volume, ma20)金融应用:多条件筛选:
# 股票筛选策略 stock = { 'name': '招商银行', 'price': 45.2, 'pe': 8.5, # 市盈率 'pb': 0.8, # 市净率 'roe': 0.15 # 净资产收益率 } # 多条件筛选 conditions = [ stock['price'] < 100, # 价格<100 stock['pe'] < 10, # PE<10 stock['pb'] < 1, # PB<1 stock['roe'] > 0.10 # ROE>10% ] # all()函数:所有条件为True时返回True if all(conditions): print(f'{stock["name"]} 符合价值投资标准') else: print(f'{stock["name"]} 不符合标准')
46.3 for循环
# 1. 遍历列表
# 创建股票代码列表
stocks = ['600519.SH', '000858.SZ', '600036.SH']
print('股票代码:')
# for循环:遍历序列中的每个元素
# 语法: for item in iterable:
for code in stocks:
# code依次取stocks中的每个元素
print(f' {code}')
# 输出:
# 600519.SH
# 000858.SZ
# 600036.SH
# 2. 遍历字典
stock_dict = {'600519.SH': '贵州茅台', '000858.SZ': '五粮液'}
print('\n股票名称:')
# items()方法返回(键, 值)元组的迭代器
# 这允许同时遍历键和值
for code, name in stock_dict.items():
# 每次迭代,code和name分别取键和值
print(f' {code}: {name}')
# 3. enumerate()获取索引和元素
print('\n带索引的遍历:')
# enumerate()返回(索引, 元素)的迭代器
# start参数指定索引起始值,默认为0
for i, code in enumerate(stocks):
# i从0开始,依次为0, 1, 2
print(f' 第{i+1}个: {code}')
# 输出:
# 第1个: 600519.SH
# 第2个: 000858.SZ
# 第3个: 600036.SH代码深度解析:
for循环的执行流程:
# for循环的等价while循环实现 iterable = ['a', 'b', 'c'] iterator = iter(iterable) # 获取迭代器 # 方式1:for循环(Python风格) for item in iterable: print(item) # 方式2:while循环(等价实现) try: while True: item = next(iterator) # 获取下一个元素 print(item) except StopIteration: pass # 迭代结束 # for循环本质上是迭代器协议的语法糖range()函数详解:
# range()生成整数序列,常用于for循环 # 语法: range(start, stop, step) # 用法1:指定结束值 for i in range(5): print(i) # 输出: 0, 1, 2, 3, 4 # 用法2:指定起始和结束值 for i in range(2, 5): print(i) # 输出: 2, 3, 4 # 用法3:指定步长 for i in range(0, 10, 2): print(i) # 输出: 0, 2, 4, 6, 8 # 金融应用:生成交易日序列 import pandas as pd dates = pd.date_range('2024-01-01', periods=5) for i in range(len(dates)): print(f'第{i+1}个交易日: {dates[i].date()}')zip()函数并行遍历:
# zip()可以同时遍历多个序列 codes = ['600519.SH', '000858.SZ', '600036.SH'] prices = [1850, 220, 45] # 同时遍历代码和价格 for code, price in zip(codes, prices): print(f'{code}: {price}元') # 使用enumerate和zip组合 for i, (code, price) in enumerate(zip(codes, prices)): print(f'{i+1}. {code}: {price}元')字典的多种遍历方式:
portfolio = { '600519.SH': {'name': '茅台', 'shares': 100}, '000858.SZ': {'name': '五粮液', 'shares': 200} } # 方式1:遍历键 print('股票代码:') for code in portfolio: print(f' {code}') # 方式2:遍历键值对 print('\n股票详情:') for code, info in portfolio.items(): print(f' {code}: {info["name"]}, {info["shares"]}股') # 方式3:遍历值 print('\n股票名称:') for info in portfolio.values(): print(f' {info["name"]}')列表推导式(List Comprehension):
# 列表推导式是for循环的简洁表达 # 语法: [expression for item in iterable if condition] # 传统for循环 squares = [] for i in range(10): squares.append(i ** 2) # 列表推导式(Python风格) squares = [i ** 2 for i in range(10)] # 带条件的列表推导式 even_squares = [i ** 2 for i in range(10) if i % 2 == 0] print(even_squares) # [0, 4, 16, 36, 64] # 金融应用:筛选符合条件的股票 stocks = [ {'name': '茅台', 'pe': 45}, {'name': '五粮液', 'pe': 35}, {'name': '招行', 'pe': 8} ] # 找出PE<20的股票 value_stocks = [s for s in stocks if s['pe'] < 20] print(f'价值股: {[s["name"] for s in value_stocks]}')
46.4 while循环
# 场景:监控股价直到达到目标价
# 定义初始变量
price = 18.00 # 当前价格
target = 20.00 # 目标价格
days = 0 # 已监控天数
# while循环:当条件为True时重复执行
# 语法: while condition:
# 注意:必须确保循环能够终止,否则会形成无限循环
while price < target:
# 每天价格增长0.50元
# += 是增量赋值操作符,等价于 price = price + 0.50
price += 0.50
# 天数加1
days += 1
# 输出当前状态
# :.2f 表示格式化为两位小数
print(f'第{days}天, 价格: {price:.2f}元')
# 安全措施:防止无限循环
# 如果超过100天仍未达到目标价,强制退出
if days > 100:
print('达到最大监控天数')
break # break语句立即跳出循环
print(f'\n达到目标价耗时{days}天')代码深度解析:
while vs for循环的选择:
# for循环:已知迭代次数 for i in range(10): print(i) # while循环:未知迭代次数,根据条件决定 i = 0 while i < 10: print(i) i += 1 # 选择原则: # - 遍历序列 → 用for # - 等待条件满足 → 用whilewhile循环的常见模式:
# 模式1:计数器循环 count = 0 while count < 5: print(count) count += 1 # 模式2:无限循环+break while True: user_input = input('输入命令(quit退出):') if user_input == 'quit': break process_command(user_input) # 模式3:标志变量 running = True while running: # 执行某些操作 if should_stop(): running = Falsewhile-else结构:
# while循环可以带else子句 # else在循环正常结束时执行(break跳出不执行) count = 0 while count < 3: print(count) count += 1 else: print('循环正常结束') # 会执行 # 对比:使用break的情况 count = 0 while count < 3: print(count) count += 1 if count == 2: break else: print('循环正常结束') # 不会执行金融应用:蒙特卡洛模拟:
import random # 模拟股价路径,直到达到止损或止盈 price = 100 stop_loss = 95 # 止损价 take_profit = 105 # 止盈价 days = 0 while stop_loss < price < take_profit: # 每日收益率服从正态分布 # 均值0.001,标准差0.02 daily_return = random.gauss(0.001, 0.02) price *= (1 + daily_return) days += 1 # 安全措施 if days > 252: # 最多模拟一年 break if price >= take_profit: print(f'第{days}天止盈, 价格: {price:.2f}元') elif price <= stop_loss: print(f'第{days}天止损, 价格: {price:.2f}元') else: print(f'第{days}天未达目标, 价格: {price:.2f}元')循环的性能考虑:
# while循环可能比for循环慢 import time # 测试for循环 start = time.time() for i in range(1000000): _ print(f'for循环: {time.time() - start:.4f}秒') # 测试while循环 start = time.time() i = 0 while i < 1000000: _ i += 1 print(f'while循环: {time.time() - start:.4f}秒') # for循环通常更快,因为迭代器协议更优化
46.5 循环控制
# 1. break语句:立即跳出循环
# 场景:查找第一个负收益并判断是否需要止损
returns = [0.05, -0.02, 0.01, -0.015, 0.03]
print('查找负收益:')
# enumerate()同时获取索引和值
for i, ret in enumerate(returns):
# 检查是否为负收益
if ret < 0:
# :.2% 表示格式化为百分比,保留两位小数
print(f' 第{i+1}天: {ret:.2%}')
# 检查是否需要止损(亏损超过1%)
if ret < -0.01:
print(' 触发止损,停止监控')
break # 立即跳出循环,不再检查后续元素
# 2. continue语句:跳过本次迭代,继续下一次
print('\n正收益日:')
for ret in returns:
# 如果是负收益,跳过本次迭代
if ret < 0:
continue # 直接进入下一次循环,不执行后面的代码
# 只有正收益才会执行这里
print(f' {ret:.2%}')代码深度解析:
break的执行流程:
# break只跳出最近的一层循环 for i in range(3): for j in range(3): if j == 1: break # 只跳出内层循环 print(f'i={i}, j={j}') # 输出: # i=0, j=0 # i=1, j=0 # i=2, j=0 # 如果要跳出多层循环,需要使用标志变量 found = False for i in range(3): if found: break for j in range(3): if j == 1: found = True breakcontinue的执行流程:
# continue跳过本次迭代的剩余代码 for i in range(5): if i % 2 == 0: continue # 跳过偶数 print(i) # 只打印奇数 # 输出: 1, 3, 5 # continue的等价写法 for i in range(5): if i % 2 != 0: # 反转条件 print(i)循环else子句与break:
# break会阻止else子句的执行 # 情况1:正常结束(执行else) for i in range(3): if i > 10: break else: print('循环正常结束,未找到') # 会执行 # 情况2:break结束(不执行else) for i in range(3): if i > 1: break else: print('循环正常结束,未找到') # 不会执行金融应用:订单撮合:
# 模拟订单撮合过程 buy_orders = [ {'price': 18.50, 'quantity': 1000}, {'price': 18.45, 'quantity': 500}, {'price': 18.40, 'quantity': 2000} ] sell_orders = [ {'price': 18.35, 'quantity': 800}, {'price': 18.40, 'quantity': 1500} ] # 按价格排序(买单降序,卖单升序) buy_orders.sort(key=lambda x: -x['price']) sell_orders.sort(key=lambda x: x['price']) # 撮合逻辑 for buy in buy_orders: for sell in sell_orders: # 如果买单价格低于卖单价格,无法成交 if buy['price'] < sell['price']: print(f'买单{buy["price"]}元低于卖单{sell["price"]}元,停止撮合') break # 跳出内层循环 # 计算成交量 trade_quantity = min(buy['quantity'], sell['quantity']) print(f'成交: 价格{sell["price"]}元, 数量{trade_quantity}股') # 更新剩余数量 buy['quantity'] -= trade_quantity sell['quantity'] -= trade_quantity # 如果卖单已完全成交,继续下一个卖单 if sell['quantity'] == 0: continue # 如果买单已完全成交,跳出内层循环 if buy['quantity'] == 0: breakpass语句:占位符:
# pass语句什么都不做,用作占位符 # 场景1:空函数体 def placeholder_function(): pass # TODO: 待实现 # 场景2:空类 class PlaceholderClass: pass # 场景3:条件分支占位 if condition: pass # 待添加代码 else: do_something()
46.6 综合应用股票筛选
# 模拟股票数据
# 每个股票是一个字典,包含代码、价格、PE等属性
stocks = [
{'code': '600519.SH', 'price': 1850, 'pe': 45},
{'code': '000858.SZ', 'price': 220, 'pe': 35},
{'code': '600036.SH', 'price': 45, 'pe': 8}
]
# 筛选策略:价格<500且PE<40的价值股
print('筛选结果:')
# 遍历所有股票
for stock in stocks:
# 条件1:价格<500(排除高价股)
if stock['price'] >= 500:
# continue:跳过本次迭代,继续下一个股票
continue
# 条件2:PE<40(合理估值)
# 只有价格条件满足的股票才会到达这里
if stock['pe'] < 40:
# 两个条件都满足,输出股票信息
print(f" {stock['code']}: 价格={stock['price']}元, PE={stock['pe']}")
# 如果PE>=40,什么都不做,继续下一个股票代码深度解析:
控制流程图:
# 本例的执行流程: # # 开始 # ↓ # 遍历stocks列表 # ↓ # 取第一个股票{'code': '600519.SH', 'price': 1850, 'pe': 45} # ↓ # 检查price>=500? # ├─ 是 → continue → 跳过,取下一个股票 # └─ 否 → 继续检查 # ↓ # 检查pe<40? # ├─ 是 → 输出股票信息 # └─ 否 → 什么都不做 # ↓ # 取下一个股票,重复上述流程 # ↓ # 所有股票处理完毕 # ↓ # 结束筛选逻辑的优化:
# 方法1:原始版本(本例使用) for stock in stocks: if stock['price'] >= 500: continue if stock['pe'] < 40: print(f"{stock['code']}: PE={stock['pe']}元") # 方法2:单个复合条件 for stock in stocks: if stock['price'] < 500 and stock['pe'] < 40: print(f"{stock['code']}: PE={stock['pe']}元") # 方法3:列表推导式(最Pythonic) filtered = [s for s in stocks if s['price'] < 500 and s['pe'] < 40] for stock in filtered: print(f"{stock['code']}: PE={stock['pe']}元") # 方法4:filter()函数(函数式风格) filtered = filter( lambda s: s['price'] < 500 and s['pe'] < 40, stocks ) for stock in filtered: print(f"{stock['code']}: PE={stock['pe']}元")多策略组合:
# 更复杂的筛选场景 stocks = [ {'code': '600519.SH', 'price': 1850, 'pe': 45, 'pb': 10, 'dividend': 1.5}, {'code': '000858.SZ', 'price': 220, 'pe': 35, 'pb': 8, 'dividend': 1.2}, {'code': '600036.SH', 'price': 45, 'pe': 8, 'pb': 0.8, 'dividend': 2.5}, {'code': '601318.SH', 'price': 50, 'pe': 9, 'pb': 0.9, 'dividend': 2.0} ] # 策略1:价值投资(低PE低PB) print('=== 价值投资策略 ===') for stock in stocks: if stock['pe'] < 20 and stock['pb'] < 1: print(f"{stock['code']}: PE={stock['pe']}, PB={stock['pb']}") # 策略2:红利策略(高股息) print('\n=== 红利策略 ===') for stock in stocks: if stock['dividend'] > 2.0: print(f"{stock['code']}: 股息率={stock['dividend']}%") # 策略3:成长策略(合理估值) print('\n=== 成长策略 ===') for stock in stocks: if 10 < stock['pe'] < 30 and stock['price'] < 100: print(f"{stock['code']}: PE={stock['pe']}, 价格={stock['price']}")性能优化:提前过滤:
# 优化前:多次遍历 results = [] for stock in stocks: if stock['price'] < 500: if stock['pe'] < 40: if stock['pb'] < 5: results.append(stock) # 优化后:将最严格的条件放在前面 # 这样可以尽早排除不符合条件的元素 results = [] for stock in stocks: # 价格条件最严格,先检查 if stock['price'] >= 500: continue # PE条件次之 if stock['pe'] >= 40: continue # PB条件最后 if stock['pb'] >= 5: continue results.append(stock)实际应用:动态止损策略:
# 动态调整止损策略 positions = [ {'code': '600519.SH', 'entry_price': 1800, 'current_price': 1850}, {'code': '000858.SZ', 'entry_price': 230, 'current_price': 220}, {'code': '600036.SH', 'entry_price': 48, 'current_price': 45} ] # 止损策略:亏损超过5%且亏损超过3元 print('止损信号:') for pos in positions: # 计算收益率 ret = (pos['current_price'] - pos['entry_price']) / pos['entry_price'] # 计算亏损金额 loss = pos['entry_price'] - pos['current_price'] # 检查止损条件 if ret < -0.05 and loss > 3: print(f"{pos['code']}: 亏损{ret:.2%}, 亏损{loss:.0f}元, 建议止损") elif ret < 0: print(f"{pos['code']}: 亏损{ret:.2%}, 继续持有") else: print(f"{pos['code']}: 盈利{ret:.2%}, 继续持有")
最佳实践总结:
- 选择合适的循环结构:
- 遍历序列 → 用
for循环 - 等待条件 → 用
while循环 - 已知次数 → 用
for+range() - 未知次数 → 用
while条件
- 遍历序列 → 用
- 避免常见陷阱:
- 无限循环:确保
while循环有终止条件 - 修改遍历对象:不要在遍历时修改列表
- 过深嵌套:超过3层嵌套考虑重构
- 循环中的计算:将不变的计算移到循环外
- 无限循环:确保
- 性能优化技巧:
- 使用列表推导式替代简单的for循环
- 使用生成器表达式处理大数据
- 将最严格的条件放在前面
- 使用内置函数(
sum(),max(),min())替代循环
- 可读性提升:
- 使用有意义的变量名
- 提取复杂条件为函数
- 添加注释说明逻辑意图
- 保持函数简短(单一职责)
平台任务解答代码
以下代码与教学平台任务要求完全一致:
# 注:该代码块存在缩进错误且读取远程平台数据文件,渲染时无法执行
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
#任务一
import pandas as pd
# 从Excel文件读取数据存入data
data=pd.read_excel("https://huoran.oss-cn-shenzhen.aliyuncs.com/1725522421307.xlsx")
DJ_index = [] # 定义列表DJ_index
HS_index= [] # 定义列表HS_index
SH_index = [] # 定义列表SH_index
SZ_index = [] # 定义列表SZ_index
for i in data["道琼斯工业平均指数"]: # 遍历data["道琼斯工业平均指数"]中的每个i
DJ_index.append(i) # 将道琼斯指数涨跌幅数据添加到列表
for i in data["恒生指数"]: #获取恒生指数的日涨跌幅列表
HS_index.append(i) # 将恒生指数涨跌幅数据添加到列表
for i in data["上证综指"]: #获取上证综指的日涨跌幅列表
SH_index.append(i) # 将上证综指涨跌幅数据添加到列表
for i in data["深证成指"]: # 遍历data["深证成指"]中的每个i
SZ_index.append(i) # 将深证成指涨跌幅数据添加到列表
print(DJ_index) # 输出指数数据
print(HS_index) # 输出指数数据
print(SH_index) # 输出指数数据
print(SZ_index) # 输出指数数据
#任务二
import pandas as pd
# 从Excel文件读取数据存入data
data=pd.read_excel("https://huoran.oss-cn-shenzhen.aliyuncs.com/1725522421307.xlsx")
DJ_index = [] # 定义列表DJ_index
for i in data["道琼斯工业平均指数"]: # 遍历data["道琼斯工业平均指数"]中的每个i
DJ_index.append(i) # 将道琼斯指数涨跌幅数据添加到列表
for i in DJ_index: # 遍历DJ_index中的每个i
if i < -0.006: #如果小于-0.006
break # 跳出循环
print("已经访问的道琼斯工业平均指数日涨跌幅数据",i) # 输出已经访问的道琼斯工业平均指数日涨跌幅数据
#任务三
import pandas as pd
# 从Excel文件读取数据存入data
data=pd.read_excel("https://huoran.oss-cn-shenzhen.aliyuncs.com/1725522421307.xlsx")
HS_index = [] # 定义列表HS_index
for i in data["恒生指数"]: # 遍历data["恒生指数"]中的每个i
HS_index.append(i) # 将恒生指数涨跌幅数据添加到列表
for i in HS_index: # 遍历HS_index中的每个i
if i > 0: #大于零的话
pass # 占位符,暂不执行任何操作
else: # 不满足以上条件时
print("恒生指数的日下跌数据(负数)",i) # 输出恒生指数的日下跌数据(负数)
#任务四
import pandas as pd
# 从Excel文件读取数据存入data
data=pd.read_excel("https://huoran.oss-cn-shenzhen.aliyuncs.com/1725522421307.xlsx")
SH_index = [] # 定义列表SH_index
SZ_index = [] # 定义列表SZ_index
for i in data["上证综指"]: # 遍历data["上证综指"]中的每个i
SH_index.append(i) # 将上证综指涨跌幅数据添加到列表
for i in data["深证成指"]: # 遍历data["深证成指"]中的每个i
SZ_index.append(i) # 将深证成指涨跌幅数据添加到列表
R_SH = [] #创建一个R_SH的空列表
for i in SH_index: # 遍历SH_index中的每个i
if i < -0.1: #如果小于-0.1
pass # 占位符,暂不执行任何操作
elif i > 0.01: #如果大于0.01
pass # 占位符,暂不执行任何操作
else: # 不满足以上条件时
R_SH.append(i) # 将上证综指筛选后的涨跌幅数据添加到列表
print("上证综指的日涨跌幅处于-1%至1%区间的列表\n",R_SH) # 输出上证综指的日涨跌幅处于-1%至1%区间的列表\n